﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

using COUP.Core.Enums;
using COUP.Core.Entities;
using COUP.Core.Database;
using COUP.Core.Cryptography;

namespace COUP.Core
{
    public class ClientState
    {        
        internal readonly ServerType ServerType;

        public readonly iDatabase Database;
        public readonly Socket Connection;
        public readonly iCryptographer Cryptographer;
        public readonly int RemoteHash;
        public readonly int LoginToken;
        public int AccountID;
        public int Infractions;
        public int Version;
        public Character Entity;
        public LoginProcess LoginState;
        public byte[] Buffer;
        public byte[] Header;
        public byte[] Body;

        public ClientState(Socket _clientSocket, int BufferSize, ServerType Type, int TargetVersion, iDatabase _database)
        {
            Buffer = new byte[BufferSize];
            Version = TargetVersion;
            ServerType = Type;
            Connection = _clientSocket;
            RemoteHash = CreateRemoteEndHash();
            LoginToken = CreateLoginToken();
            Database = _database;

            if (ServerType == ServerType.Auth)
                Cryptographer = new AuthCrypt();
            else
            {
                switch (Version)
                {
                    case 4267:
                        Cryptographer = new GameCrypt(); break;
                    default:
                        throw new NotImplementedException("The Version you have specified is not implemented at this time.");
                }
            }
        }

        public void Send(Packet Data)
        {
#if DEBUG
            Console.WriteLine("[ClientState] Sending Packet: {0}", Data.Type);
#endif            
            Cryptographer.Encrypt(ref Data.buffer);
            Connection.Send(Data.Buffer);
            Data.Dispose();
        }

        public void SendToLocal(Packet Data, bool Self=false)
        {
            foreach (iEntity Target in Entity.GetLocalEntities)
            {
                if (Target is Character)
                {
                    Character Char = Target as Character;

                    if (Char.UniqueID == Entity.UniqueID && !Self)
                        continue;

                    Char.Parent.Send(new Packet(Data.Buffer));
                }
            }
        }

        public void Disconnect()
        {
            Connection.Disconnect(false);
            Dispose();
        }

        public void Dispose()
        {
            GC.Collect();
        }

        internal int CreateLoginToken()
        {
            int Token = (int)new Random().Next(10000000) << 32;
            Token = Token << 32;
            Token = (int)Token | (int)new Random().Next(10000000);
            return Token;
        }
            
        internal int CreateRemoteEndHash()
        {
            string EP = Connection.RemoteEndPoint.ToString();
            string[] EPs = EP.Split('.');
            int Result = 0;
            for (byte x = 0; x < 3; x++)
            {
                Result += int.Parse(EPs[x]);
                Result = Result << x * 6;
            }
            return Result;
        }
    }
}
